#include "CRServerSocket.h"


CRServerSocket::CRServerSocket()
{
	m_nOpenPort = 0;
	m_nMaxClient = 0;	
	m_hSSocket = INVALID_SOCKET;
	m_AfterRunFunc = NULL;
	m_AfterConnectFunc = NULL;
	m_AfterDisconnectFunc = NULL;
	m_AfterRecvFunc = NULL;
}

CRServerSocket::~CRServerSocket() 
{
	for(ClientRC_MAP_TYPE::iterator mt = m_mapClientRC.begin();  mt != m_mapClientRC.end(); mt++) {
		closesocket(mt->first);
		delete mt->second;
	}
	m_mapClientRC.clear();
	if(m_hSSocket != INVALID_SOCKET) {
		closesocket(m_hSSocket);
	}
}


int CRServerSocket::CreateServer(int nPort, int nMaxClient)
{
	if(m_hSSocket != INVALID_SOCKET)
		return ALREADY_CREATE;
	
	//Init setting
	WSADATA WsaData;
	WSAStartup(0x202, &WsaData);	//Socket 2.2
	m_hSSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);	//TCP/IP
	if(m_hSSocket == INVALID_SOCKET) 
		return INIT_SOCKET_ERROR;
	u_long non_blocking = 1;
	ioctlsocket(m_hSSocket, FIONBIO, &non_blocking);

	//Binding
	sockaddr_in ServerInfo;
	ServerInfo.sin_family = AF_INET;
	ServerInfo.sin_addr.s_addr = INADDR_ANY;
	ServerInfo.sin_port = htons(nPort);
	int nRet = bind(m_hSSocket, (struct sockaddr*) &ServerInfo, sizeof(struct sockaddr));
	if(nRet == SOCKET_ERROR)
		return BINDING_ERROR;

	//Set listen
	nRet = listen(m_hSSocket, nMaxClient);
	if(nRet == SOCKET_ERROR)
		return SET_LISTEN_ERROR;

	m_nOpenPort = nPort;	//Open for connecting port.
	m_nMaxClient = nMaxClient;	//Max client number.
	
	return CREATE_SUCCESS;
}

int CRServerSocket::Run()
{
	static fd_set SReadfd;
	static timeval TimeOut = {0, 0,};  

	FD_ZERO(&SReadfd);
	FD_SET(m_hSSocket, &SReadfd);
	
	int nRet = select((int)m_hSSocket + 1, &SReadfd, NULL, NULL, &TimeOut);
	if(nRet > 0 && FD_ISSET(m_hSSocket, &SReadfd)) {
		sockaddr_in ClientInfo;
		int size = sizeof(ClientInfo);
		SOCKET ClientSocket = accept(m_hSSocket, (struct sockaddr*)&ClientInfo, &size);
		if(ClientSocket != INVALID_SOCKET) {
			CClinetRC *pNewClientRC = new CClinetRC(ClientSocket, ClientInfo, DEFAULT_RCEV_BUF_SIZE);
			m_mapClientRC.insert(ClientRC_MAP_TYPE::value_type(ClientSocket, pNewClientRC));
			
			if(m_AfterConnectFunc)	//callback function
				m_AfterConnectFunc(pNewClientRC);
		}
	}

	static char buf[1024];
	static int buflen = 1024;
	ClientRC_MAP_TYPE::iterator mt = m_mapClientRC.begin();
	bool bClostThisSocket = false;
	while(mt != m_mapClientRC.end()) {
		bClostThisSocket = false;
		nRet = recv(mt->first, buf, buflen, 0);
		if(nRet > 0) {
			//Got data..
			if(mt->second->m_pcClientRecvBuf->WriteDataMakeAllSure((BYTE *)buf, nRet) < 0) {
				//Buffer Full..	
				bClostThisSocket = true;
			}
			if(m_AfterRecvFunc)	//callback function
				m_AfterRecvFunc(mt->second);
		}
		else if(nRet == 0) {
			//Client closed socket.
			//If the connection has been gracefully closed, the return value is zero
			bClostThisSocket = true;
		}
		else {
			//Something wrong.. or NON_BLOCKING
			int nErrno = WSAGetLastError();
			if(nErrno != WSAEWOULDBLOCK)
				bClostThisSocket = true;
		}
		if(bClostThisSocket) {
			SOCKET tmpSock = mt->first;
			closesocket(mt->first);
			if(m_AfterDisconnectFunc)	//callback function
				m_AfterDisconnectFunc(mt->second);
			delete mt->second;
			mt++;	//Must switch to next one befor erases, otherwise it will crash.
			m_mapClientRC.erase(tmpSock);
		}
		else
			mt++;
	}

	if(m_AfterRunFunc)	//callback function
		m_AfterRunFunc(this);

	return 0;
}

void CRServerSocket::SetAfterRun(void (*AfterRunFunc)(CRServerSocket *pCRSS))
{
	if(AfterRunFunc)
		m_AfterRunFunc = AfterRunFunc;
}

void CRServerSocket::SetAfterConnect(void (*AfterConnectFunc)(CClinetRC *pCCRC))
{
	if(AfterConnectFunc)
		m_AfterConnectFunc = AfterConnectFunc;
}

void CRServerSocket::SetAfterDisconnect(void (*AfterDisonnectFunc)(CClinetRC *pCCRC))
{
	if(AfterDisonnectFunc)
		m_AfterDisconnectFunc = AfterDisonnectFunc;
}

void CRServerSocket::SetAfterRecv(void (*AfterRecvFunc)(CClinetRC *pCCRC))
{
	if(AfterRecvFunc)
		m_AfterRecvFunc = AfterRecvFunc;
}